github.com/quinndk/ethereum_read@v0.0.0-20181211143958-29c55eec3237/Docs/0x09 ethash和clique共识引擎.md (about)

     1  # 0x09 ethash和clique共识引擎
     2  
     3  # Consensus以太坊共识引擎
     4  
     5  之前讲了以太坊的挖矿逻辑,还没真正涉及到POW的实现。上面也讲到过以太坊的共识引擎有两种,ethash和clique。ethash本质上就是一种POW算法,clique则是POA(ProofOfAuthortiy)算法。目前以太坊采用的是ethash共识引擎,也就是POW算法。今天分别看下两种共识算法的源码实现。
     6  
     7  # Ethash算法
     8  
     9  ### 原理
    10  
    11  抛开其内部实现哈希的算法,其基本原理可以用一个公式表示:
    12  
    13  > #### RAND(h, nonce) <= M / d
    14  
    15  这里h表示区块头的哈希,nonce表示一个自增的变量,RAND表示经过一系列算法生成一个类似随机数的函数。
    16  M表示一个极大的数,d则是当前区块的难度值header.diffculty。
    17  
    18  但是涉及到ethash内部哈希值的计算方式就不得不提以下几个概念。
    19  
    20  #### DAG
    21  说到这想起一个题外话,之前看到网上有人问比特币矿机能用来挖以太币吗。怎么说呢,有钱任性的话也是可以的,因为这样做的结果是入不敷出。这是为什么呢?
    22  
    23  Bitcoin的POW是完全基于算力的,而Ethash则是基于内存和算力的,它和计算机的内存大小和内存带宽正相关。计算能力再强,它每次读取内存的带宽是有限的,这就是为什么即使用来昂贵的ASIC矿机来挖以太币,收益也不会比PC号多少。但是 ,道高一尺魔高一丈,据说比特大陆已经研发出用于以太坊的专业矿机,不过价格不菲每台800美元。题外话就说到这,接着回归正题。
    24  
    25  我们知道POW算法依赖一个nonce值输入算法来得到一个低于困难度阈值的哈希值,Eth引入了**[DAG](https://github.com/ethereum/wiki/wiki/Ethash-DAG)**来提供一个大的,瞬态,依赖于块头哈希和Nonce随机生成的固定资源的子集来参与最终哈希值的计算。
    26  
    27  DAG资源大约占用1GB大小的内存。 其文件存储路径为:
    28  
    29  - Mac/Linux:$(HOME)/.ethash/full-R<REVISION>-<SEEDHASH>
    30  
    31  - Windows: $(HOME)/Appdata/Local/Ethash/full-R<REVISION>-<SEEDHASH>
    32  
    33  其文件目录命名的意义为:
    34  
    35  - <REVISION>:一个十进制整数,当前的修订版本
    36  - <SEEDHASH> :16个小写的十六进制数字,指定了当前epoch(在以太坊中每30000个区块会生成一个DAG,这个DAG被称为epoch,大约5.2d,125h)种子哈希的前8个字节
    37  
    38  DAG文件的内部格式:
    39  
    40  - 1.little-endian小端模式存储(每个unint32以little-endian格式编码)
    41  
    42  - 2.headerBytes:8-byte magic number(0xfee1deadbaddcafe)
    43  
    44  - 3.DAG是一个uint32s类型的二维数组,维度是N * 16,N是一个幻数(从16777186开始)
    45  
    46  - 4.DAG的行应按顺序写入文件,行之间没有分隔符
    47  
    48  下图便是我Mac上的几个DAG文件:
    49  
    50  ![DAG路径](https://upload-images.jianshu.io/upload_images/830585-43a196f0aef1003e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    51  
    52  ethash依赖于DAG实现POW,从[mining的wiki](https://github.com/ethereum/wiki/wiki/Mining#ethash-dag)中得知:DAG需要很长时间才能生成。如果客户端仅按需生成它,可能会在找到新纪元的第一个块之前看到每个纪元转换的漫长等待。一般会预先计算DAG来避免在每个epoch过渡时发生过长的等待时间。geth执行自动的DAG生成,每次维持两个DAG来保障epoch过渡流畅。
    53  
    54  #### Dagger-Hashimoto
    55  ethash算法,又叫Dashimoto (Dagger-Hashimoto),是Hashimoto算法结合Dagger之后产成的一个变种。其实,在最开始以太坊使用的POW就叫[Dagger-Hashimoto](https://github.com/ethereum/wiki/wiki/Dagger-Hashimoto)。后来,进一步改进后便产生了被命名为[ethash](https://github.com/ethereum/wiki/wiki/Ethash)的Dashimoto算法最新版本。这里暂且以最新的ethash为例去分析,旧版本有兴趣的可以自己看看。
    56  
    57  
    58  [ethash设计的初衷](https://github.com/ethereum/wiki/wiki/Ethash-Design-Rationale)旨在实现以下目标:
    59  
    60  - __1.IO饱和度__:算法应消耗几乎所有可用的内存访问带宽(有效的抵抗ASIC)
    61  
    62  - __2.GPU友好__:尽可能地使用GPU进行挖矿。
    63  
    64  - __3.轻客户端可验证性__:轻客户端应该能够在0.01秒内验证一轮挖掘的有效性,Python或Javascript中验证不到0.1秒,最多1 MB内存(但指数增加)
    65  
    66  - __4.轻客户端减速__:使用轻客户端运行算法的过程应该比使用完整客户端的过程慢得多,以至于轻客户端算法不是经济上可行的实现挖掘实现的途径,包括通过专用硬件
    67  
    68  - __5.轻客户端快速启动__:轻客户端应该能够完全运行并能够在40秒内在Javascript中验证块
    69  
    70  ethash的大概流程是这样的:
    71  
    72  - __1.先根据block number以及block header计算出一个种子值seed__
    73  
    74  - 2.__使用seed产生一个16MB的伪随机数集cache__
    75  
    76  - 3.__根据cache生成一个1GB的数据集DAG(可以根据cache快速定位DAG中指定位置的元素,所以一般轻客户端保存cache,完整客户端保存DAG)__
    77  
    78  - 4.__从DAG中随机选择元素对其进行hash,然后判断哈希值是否小于给定值__
    79  
    80  - 5.__cache和DAG每个周期(1000个块)更新一次。DAG从1GB开始随着时间线性增长,现在好像达到20多GB了__
    81  
    82  ### 源码撸起来
    83  
    84  [wiki](https://github.com/ethereum/wiki/wiki/Ethash)里关于ethash代码的分析是以Python为例的,这里我们看官方go-ethereum里的源码实现。
    85  
    86  ```
    87  // Ethash is a consensus engine based on proof-of-work implementing the ethash
    88  // algorithm.
    89  type Ethash struct {
    90  
    91  	// ethash配置
    92  	config Config
    93  
    94  	// 内存缓存,可反复使用避免再生太频繁
    95  	caches   *lru // In memory caches to avoid regenerating too often
    96  	// 内存数据集
    97  	datasets *lru // In memory datasets to avoid regenerating too often
    98  
    99  	// Mining related fields
   100  	// 随机工具,用来生成种子
   101  	rand     *rand.Rand    // Properly seeded random source for nonces
   102  	// 挖矿的线程数
   103  	threads  int           // Number of threads to mine on if mining
   104  	// 挖矿通道
   105  	update   chan struct{} // Notification channel to update mining parameters
   106  	// 平均哈希率
   107  	hashrate metrics.Meter // Meter tracking the average hashrate
   108  
   109  	// The fields below are hooks for testing
   110  	// 共享pow,无法再生缓存
   111  	shared    *Ethash       // Shared PoW verifier to avoid cache regeneration
   112  	// 未通过pow的区块号,包括fakeMode
   113  	fakeFail  uint64        // Block number which fails PoW check even in fake mode
   114  	// 验证工作返回消息前的延迟时间
   115  	fakeDelay time.Duration // Time delay to sleep for before returning from verify
   116  	
   117  	// 同步锁
   118  	lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
   119  }
   120  
   121  // New creates a full sized ethash PoW scheme.
   122  // 生成ethash对象
   123  func New(config Config) *Ethash {
   124  	if config.CachesInMem <= 0 {
   125  		log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
   126  		config.CachesInMem = 1
   127  	}
   128  	if config.CacheDir != "" && config.CachesOnDisk > 0 {
   129  		log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
   130  	}
   131  	if config.DatasetDir != "" && config.DatasetsOnDisk > 0 {
   132  		log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
   133  	}
   134  	return &Ethash{
   135  		config:   config,
   136  		caches:   newlru("cache", config.CachesInMem, newCache),
   137  		datasets: newlru("dataset", config.DatasetsInMem, newDataset),
   138  		update:   make(chan struct{}),
   139  		hashrate: metrics.NewMeter(),
   140  	}
   141  }
   142  ```
   143  
   144  ### generateDataset-DAG生成 
   145  这里涉及到两个存储数据的类cache和dataset,他们的数据结构类似。我猜想这里的cache对应ethash需要的缓存cache,dataset对应相应的DAG。
   146  
   147  ```
   148  // cache wraps an ethash cache with some metadata to allow easier concurrent use.
   149  // cache使用一些元数据包装ethash缓存,以便更容易并发使用。
   150  type cache struct {
   151  	// 属于哪一个epoch
   152  	epoch uint64    // Epoch for which this cache is relevant
   153  	// 该内存存储于磁盘的文件对象
   154  	dump  *os.File  // File descriptor of the memory mapped cache
   155  	// 内存映射
   156  	mmap  mmap.MMap // Memory map itself to unmap before releasing
   157  	// 实际使用的内存
   158  	cache []uint32  // The actual cache data content (may be memory mapped)
   159  	once  sync.Once // Ensures the cache is generated only once
   160  }
   161  ...
   162  // dataset wraps an ethash dataset with some metadata to allow easier concurrent use.
   163  type dataset struct {
   164  	epoch   uint64    // Epoch for which this cache is relevant
   165  	dump    *os.File  // File descriptor of the memory mapped cache
   166  	mmap    mmap.MMap // Memory map itself to unmap before releasing
   167  	dataset []uint32  // The actual cache data content
   168  	once    sync.Once // Ensures the cache is generated only once
   169  }
   170  ```
   171  
   172  上面说了,在计算块头和nonce的哈希时需要用到DAG数据集。这时,会首先从磁盘检索对应需要的DAG文件,有则加载,没有的话就新建一个DAG数据集。
   173  
   174  ```
   175  // dataset tries to retrieve a mining dataset for the specified block number
   176  // by first checking against a list of in-memory datasets, then against DAGs
   177  // stored on disk, and finally generating one if none can be found.
   178  // 在磁盘上找到一个DAG,如果没有则创建
   179  func (ethash *Ethash) dataset(block uint64) *dataset {
   180  	// 计算当前对应的epoch
   181  	epoch := block / epochLength
   182  	currentI, futureI := ethash.datasets.get(epoch)
   183  	current := currentI.(*dataset)
   184  
   185  	// Wait for generation finish.
   186  	// 这里有的话会直接加载,没有的话才会真的创建  详见generate代码
   187  	current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
   188  
   189  	// If we need a new future dataset, now's a good time to regenerate it.
   190  	// 创建一个将来的DAG以保障epoch过渡流畅
   191  	if futureI != nil {
   192  		future := futureI.(*dataset)
   193  		go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
   194  	}
   195  
   196  	return current
   197  }
   198  ```
   199  
   200  这里有个常量epochLength,我们先来看下有关它的定义。因为后面源码用到了许多类似的常量,我们必须清楚他们表示的意义。
   201  
   202  ```
   203  const (
   204  	// 初始dataset的字节数 1GB
   205  	datasetInitBytes   = 1 << 30 // Bytes in dataset at genesis
   206  	// 每epoch dataset数据集的增长量
   207  	datasetGrowthBytes = 1 << 23 // Dataset growth per epoch
   208  	// 初始cache的字节数 16MB
   209  	cacheInitBytes     = 1 << 24 // Bytes in cache at genesis
   210  	// 每epoch cache的字节数增长
   211  	cacheGrowthBytes   = 1 << 17 // Cache growth per epoch
   212  	// 每个epoch包含的区块数
   213  	epochLength        = 30000   // Blocks per epoch
   214  	// mix位宽
   215  	mixBytes           = 128     // Width of mix
   216  	// hash长度
   217  	hashBytes          = 64      // Hash length in bytes
   218  	// 散列中的32位无符号整数的个数 DAG是个uint32s类型的二维数组,每个uint32s的元素个数为hashWords
   219  	hashWords          = 16      // Number of 32 bit ints in a hash
   220  	// 每个数据集元素datasetItem的父数  从datasetParents个伪随机选择的缓存数据得到一个datasetItem
   221  	datasetParents     = 256     // Number of parents of each dataset element
   222  	// 一次cache生成的循环次数
   223  	cacheRounds        = 3       // Number of rounds in cache production
   224  	// hashimoto循环中的访问次数
   225  	loopAccesses       = 64      // Number of accesses in hashimoto loop
   226  )
   227  ```
   228  
   229  接着深入到generate函数,这里也可以从代码中看到它在磁盘里的存储路径。
   230  
   231  ```
   232  // generate ensures that the dataset content is generated before use.
   233  // 确保在使用之前生成DAG数据集
   234  func (d *dataset) generate(dir string, limit int, test bool) {
   235  	d.once.Do(func() {
   236  
   237  		//cache和dataset集合(DAG)大小计算
   238  		csize := cacheSize(d.epoch*epochLength + 1)
   239  		dsize := datasetSize(d.epoch*epochLength + 1)
   240  		seed := seedHash(d.epoch*epochLength + 1)
   241  		if test {
   242  			csize = 1024
   243  			dsize = 32 * 1024
   244  		}
   245  		// If we don't store anything on disk, generate and return
   246  		// 目前DAG目录里还不存在DAG文件,则根据cache创建DAG
   247  		if dir == "" {
   248  			cache := make([]uint32, csize/4)
   249  			generateCache(cache, d.epoch, seed)
   250  
   251  			d.dataset = make([]uint32, dsize/4)
   252  			generateDataset(d.dataset, d.epoch, cache)
   253  		}
   254  		// Disk storage is needed, this will get fancy
   255  		// 需要磁盘存储
   256  		var endian string
   257  		if !isLittleEndian() {
   258  			endian = ".be"
   259  		}
   260  		path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian))
   261  		logger := log.New("epoch", d.epoch)
   262  
   263  		// We're about to mmap the file, ensure that the mapping is cleaned up when the
   264  		// cache becomes unused.
   265  		runtime.SetFinalizer(d, (*dataset).finalizer)
   266  
   267  		// Try to load the file from disk and memory map it
   268  		// 加载DAG文件并将内存映射到它
   269  		var err error
   270  		d.dump, d.mmap, d.dataset, err = memoryMap(path)
   271  		if err == nil {
   272  			logger.Debug("Loaded old ethash dataset from disk")
   273  			return
   274  		}
   275  		logger.Debug("Failed to load old ethash dataset", "err", err)
   276  
   277  		// No previous dataset available, create a new dataset file to fill
   278  		// 没有以前的可用数据集,创建要填充的数据集文件
   279  		cache := make([]uint32, csize/4)
   280  		generateCache(cache, d.epoch, seed)
   281  
   282  		d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) })
   283  		if err != nil {
   284  			logger.Error("Failed to generate mapped ethash dataset", "err", err)
   285  
   286  			d.dataset = make([]uint32, dsize/2)
   287  			generateDataset(d.dataset, d.epoch, cache)
   288  		}
   289  		// Iterate over all previous instances and delete old ones
   290  		// 迭代更新DAG文件并删除过于老旧的
   291  		for ep := int(d.epoch) - limit; ep >= 0; ep-- {
   292  			seed := seedHash(uint64(ep)*epochLength + 1)
   293  			path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian))
   294  			os.Remove(path)
   295  		}
   296  	})
   297  }
   298  ```
   299  
   300  那一个DAG集合具体是怎么生成的呢,这就要看generateDataset的内部代码了。
   301  
   302  ```
   303  // generateDataset generates the entire ethash dataset for mining.
   304  // This method places the result into dest in machine byte order.
   305  // 创建用于挖掘的DAG数据集,将结果按机器字节顺序存放到dest
   306  func generateDataset(dest []uint32, epoch uint64, cache []uint32) {
   307  	// Print some debug logs to allow analysis on low end devices
   308  	logger := log.New("epoch", epoch)
   309  
   310  	start := time.Now()
   311  	defer func() {
   312  		elapsed := time.Since(start)
   313  
   314  		logFn := logger.Debug
   315  		if elapsed > 3*time.Second {
   316  			logFn = logger.Info
   317  		}
   318  		logFn("Generated ethash verification cache", "elapsed", common.PrettyDuration(elapsed))
   319  	}()
   320  
   321  	// Figure out whether the bytes need to be swapped for the machine
   322  	// 判断是否是小端模式
   323  	swapped := !isLittleEndian()
   324  
   325  	// Convert our destination slice to a byte buffer
   326  	// 将dest转化为字节缓冲区dataset
   327  	header := *(*reflect.SliceHeader)(unsafe.Pointer(&dest))
   328  	header.Len *= 4
   329  	header.Cap *= 4
   330  	dataset := *(*[]byte)(unsafe.Pointer(&header))
   331  
   332  	// Generate the dataset on many goroutines since it takes a while
   333  	// 我们知道一个dataset庞大到需要占用月1GB空间
   334  	// 所以这里为了加快创建速度,开启多协程来创建它
   335  
   336  	// 协程数
   337  	threads := runtime.NumCPU()
   338  	size := uint64(len(dataset))
   339  
   340  	// 创建一个等待信号量,目的是为了等所有协程执行完毕再结束主协程
   341  	var pend sync.WaitGroup
   342  	pend.Add(threads)
   343  
   344  	var progress uint32
   345  	for i := 0; i < threads; i++ {
   346  		go func(id int) {
   347  			defer pend.Done()
   348  
   349  			// Create a hasher to reuse between invocations
   350  			// 一个在调用之间重用的哈希
   351  			keccak512 := makeHasher(sha3.NewKeccak512())
   352  
   353  			// Calculate the data segment this thread should generate
   354  			// 计算当前协程应该计算的数据量
   355  			batch := uint32((size + hashBytes*uint64(threads) - 1) / (hashBytes * uint64(threads)))
   356  			first := uint32(id) * batch
   357  			limit := first + batch
   358  			if limit > uint32(size/hashBytes) {
   359  				limit = uint32(size / hashBytes)
   360  			}
   361  			// Calculate the dataset segment
   362  			// 计算dataset数据
   363  			percent := uint32(size / hashBytes / 100)
   364  			for index := first; index < limit; index++ {
   365  
   366  				// dataset数据集由若干个datasetItem组成
   367  				item := generateDatasetItem(cache, index, keccak512)
   368  
   369  				// 字节序反转,以保证最后按小端模式存储
   370  				if swapped {
   371  					swap(item)
   372  				}
   373  				//将计算好的一个datasetItem添加到dataset
   374  				copy(dataset[index*hashBytes:], item)
   375  
   376  				if status := atomic.AddUint32(&progress, 1); status%percent == 0 {
   377  					logger.Info("Generating DAG in progress", "percentage", uint64(status*100)/(size/hashBytes), "elapsed", common.PrettyDuration(time.Since(start)))
   378  				}
   379  			}
   380  		}(i)
   381  	}
   382  	// Wait for all the generators to finish and return
   383  	pend.Wait()
   384  }
   385  ...
   386  // generateDatasetItem combines data from 256 pseudorandomly selected cache nodes,
   387  // and hashes that to compute a single dataset node.
   388  // 组合来自256个伪随机选择的缓存节点的数据,用于计算单个数据集节点的哈希值
   389  func generateDatasetItem(cache []uint32, index uint32, keccak512 hasher) []byte {
   390  	// Calculate the number of theoretical rows (we use one buffer nonetheless)
   391  	// 计算理论行数
   392  	rows := uint32(len(cache) / hashWords)
   393  
   394  	// Initialize the mix
   395  	mix := make([]byte, hashBytes)
   396  
   397  	binary.LittleEndian.PutUint32(mix, cache[(index%rows)*hashWords]^index)
   398  	for i := 1; i < hashWords; i++ {
   399  		binary.LittleEndian.PutUint32(mix[i*4:], cache[(index%rows)*hashWords+uint32(i)])
   400  	}
   401  	keccak512(mix, mix)
   402  
   403  	// Convert the mix to uint32s to avoid constant bit shifting
   404  	// 将mix转换为uint32类型,前面讲过DAG是一个uint32s类型的二维数组
   405  	intMix := make([]uint32, hashWords)
   406  	for i := 0; i < len(intMix); i++ {
   407  		intMix[i] = binary.LittleEndian.Uint32(mix[i*4:])
   408  	}
   409  	// fnv it with a lot of random cache nodes based on index
   410  	// fnv运算从256个伪随机选择的缓存节点的数据得到一个datasetItem的值
   411  	for i := uint32(0); i < datasetParents; i++ {
   412  		parent := fnv(index^i, intMix[i%16]) % rows
   413  		fnvHash(intMix, cache[parent*hashWords:])
   414  	}
   415  	// Flatten the uint32 mix into a binary one and return
   416  	// uint32转化成二进制小端格式
   417  	for i, val := range intMix {
   418  		binary.LittleEndian.PutUint32(mix[i*4:], val)
   419  	}
   420  	keccak512(mix, mix)
   421  	return mix
   422  }
   423  ```
   424  
   425  ### Seal()实现ethash
   426  
   427  我们上面分析挖矿逻辑时,分析到了agent内部是通过Engine.seal来实现挖矿的。这里我们就可以来看看ethash的Seal()实现。
   428  
   429  ```
   430  // Seal implements consensus.Engine, attempting to find a nonce that satisfies
   431  // the block's difficulty requirements.
   432  // Seal实现了共识引擎,找到满足块的难度要求的Nonce值。
   433  func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
   434  	// If we're running a fake PoW, simply return a 0 nonce immediately
   435  	// ModeFake模式立即返回
   436  	if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
   437  		header := block.Header()
   438  		header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{}
   439  		return block.WithSeal(header), nil
   440  	}
   441  	// If we're running a shared PoW, delegate sealing to it
   442  	// 共享模式,转到它的共享对象执行Seal操作
   443  	if ethash.shared != nil {
   444  		return ethash.shared.Seal(chain, block, stop)
   445  	}
   446  	// Create a runner and the multiple search threads it directs
   447  	// 创建runner和它指挥的多重搜索线程
   448  	abort := make(chan struct{})
   449  	found := make(chan *types.Block)
   450  
   451  	// 线程锁
   452  	ethash.lock.Lock()
   453  	// 获取挖矿线程
   454  	threads := ethash.threads
   455  	if ethash.rand == nil {
   456  		// 获取种子seed
   457  		seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
   458  		if err != nil {
   459  			ethash.lock.Unlock()
   460  			return nil, err
   461  		}
   462  		// 执行成功,拿到合法种子seed,通过其获得rand对象,赋值
   463  		ethash.rand = rand.New(rand.NewSource(seed.Int64()))
   464  	}
   465  	ethash.lock.Unlock()
   466  	if threads == 0 {
   467  		// 如果设定的线程数为0,则实际线程数同CPU数
   468  		threads = runtime.NumCPU()
   469  	}
   470  	if threads < 0 {
   471  		// 允许在本地/远程周围禁用本地挖掘而无需额外的逻辑
   472  		threads = 0 // Allows disabling local mining without extra logic around local/remote
   473  	}
   474  
   475  	// 创建一个计数的信号量
   476  	var pend sync.WaitGroup
   477  	for i := 0; i < threads; i++ {
   478  		//信号量赋值
   479  		pend.Add(1)
   480  		go func(id int, nonce uint64) {
   481  			// 信号量值减1
   482  			defer pend.Done()
   483  			// 挖矿工作
   484  			ethash.mine(block, id, nonce, abort, found)
   485  		}(i, uint64(ethash.rand.Int63()))
   486  	}
   487  	// Wait until sealing is terminated or a nonce is found
   488  	// 一直等到找到符合条件的nonce值
   489  	var result *types.Block
   490  	select {
   491  	case <-stop:
   492  		// Outside abort, stop all miner threads
   493  		// 停止信号
   494  		close(abort)
   495  	case result = <-found:
   496  		// One of the threads found a block, abort all others
   497  		// 其中有线程找到了合法区块
   498  		close(abort)
   499  	case <-ethash.update:
   500  		// Thread count was changed on user request, restart
   501  		// 重启信号
   502  		close(abort)
   503  		pend.Wait()
   504  		return ethash.Seal(chain, block, stop)
   505  	}
   506  	// Wait for all miners to terminate and return the block
   507  	// 等待所有矿工终止并返回该区块
   508  	// Wait判断信号量计数器大于0,就会阻塞
   509  	pend.Wait()
   510  	return result, nil
   511  }
   512  
   513  // mine is the actual proof-of-work miner that searches for a nonce starting from
   514  // seed that results in correct final block difficulty.
   515  // 实际的POW,从种子开始搜索一个nonce,直到正确的合法区块出现
   516  func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) {
   517  	// Extract some data from the header
   518  	// 从区块头中取出一些数据
   519  	var (
   520  		header  = block.Header()
   521  		hash    = header.HashNoNonce().Bytes()
   522  		target  = new(big.Int).Div(maxUint256, header.Difficulty)
   523  		number  = header.Number.Uint64()
   524  		dataset = ethash.dataset(number)
   525  	)
   526  	// Start generating random nonces until we abort or find a good one
   527  	// 开始生成随机的随机数,直到我们中止或找到一个合法的
   528  	var (
   529  		// 初始化一个变量来表示尝试次数
   530  		attempts = int64(0)
   531  		// 初始化nonce值,后面该值会递增
   532  		nonce    = seed
   533  	)
   534  	logger := log.New("miner", id)
   535  	logger.Trace("Started ethash search for new nonces", "seed", seed)
   536  search:
   537  	for {
   538  		select {
   539  		case <-abort:
   540  			// Mining terminated, update stats and abort
   541  			// 停止信号
   542  			logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed)
   543  			ethash.hashrate.Mark(attempts)
   544  			break search
   545  
   546  		default:
   547  			// We don't have to update hash rate on every nonce, so update after after 2^X nonces
   548  			// 不必更新每个nonce的哈希率,所以在2 ^ X nonces之后更新
   549  			attempts++
   550  			if (attempts % (1 << 15)) == 0 {
   551  				// 尝试次数达到2^15,更新hashrate 
   552  				ethash.hashrate.Mark(attempts)
   553  				attempts = 0
   554  			}
   555  			// Compute the PoW value of this nonce
   556  			// 计算当前nonce的pow值
   557  			digest, result := hashimotoFull(dataset.dataset, hash, nonce)
   558  			if new(big.Int).SetBytes(result).Cmp(target) <= 0 {
   559  				// Correct nonce found, create a new header with it
   560  				// 找到合法的nonce值,为header赋值
   561  				header = types.CopyHeader(header)
   562  				header.Nonce = types.EncodeNonce(nonce)
   563  				header.MixDigest = common.BytesToHash(digest)
   564  
   565  				// Seal and return a block (if still needed)
   566  				select {
   567  				case found <- block.WithSeal(header):
   568  					logger.Trace("Ethash nonce found and reported", "attempts", nonce-seed, "nonce", nonce)
   569  				case <-abort:
   570  					logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce)
   571  				}
   572  				break search
   573  			}
   574  			nonce++
   575  		}
   576  	}
   577  	// Datasets are unmapped in a finalizer. Ensure that the dataset stays live
   578  	// during sealing so it's not unmapped while being read.
   579  	runtime.KeepAlive(dataset)
   580  }
   581  ```
   582  
   583  在mine()中hashimotoFull()是为nonce值计算pow的算法,我们继续深入到这个函数中。在它的附近我们发现一个hashimotoLight()函数,这两个函数的入参结构相同,内部代码的处理逻辑也相同。不同的是,前者传的是cache数据集,后者是dataset数据集,再结合名字可以猜测hashimotoLight是hashimotoFull的轻量级实现。
   584  
   585  ```
   586  // hashimotoFull aggregates data from the full dataset (using the full in-memory
   587  // dataset) in order to produce our final value for a particular header hash and
   588  // nonce.
   589  // 在传入的数据集中通过hash和nonce值计算加密值
   590  func hashimotoFull(dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte) {
   591  	// 定义一个lookup函数,用于在数据集中查找数据
   592  	lookup := func(index uint32) []uint32 {
   593  		offset := index * hashWords
   594  		return dataset[offset : offset+hashWords]
   595  	}
   596  
   597  	// 将原始数据集进行了读取分割,然后传给hashimoto函数
   598  	return hashimoto(hash, nonce, uint64(len(dataset))*4, lookup)
   599  }
   600  ...
   601  // hashimoto aggregates data from the full dataset in order to produce our final
   602  // value for a particular header hash and nonce.
   603  // 在传入的数据集中通过hash和nonce值计算加密值
   604  func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32) []uint32) ([]byte, []byte) {
   605  	// Calculate the number of theoretical rows (we use one buffer nonetheless)
   606  	// 计算数据集理论的行数
   607  	rows := uint32(size / mixBytes)
   608  
   609  	// Combine header+nonce into a 64 byte seed
   610  	// 合并header和nonce到一个40bytes的seed
   611  	seed := make([]byte, 40)
   612  	copy(seed, hash)
   613  	binary.LittleEndian.PutUint64(seed[32:], nonce)
   614  
   615  	// 将seed进行Keccak512加密
   616  	seed = crypto.Keccak512(seed)
   617  	// 从seed中获取区块头
   618  	seedHead := binary.LittleEndian.Uint32(seed)
   619  
   620  	// Start the mix with replicated seed
   621  	// 开始与重复seed的混合 mixBytes/4 = 128/4=32
   622  	// 长度32,元素uint32 mix占4*32=128bytes
   623  	mix := make([]uint32, mixBytes/4)
   624  	for i := 0; i < len(mix); i++ {
   625  		mix[i] = binary.LittleEndian.Uint32(seed[i%16*4:])
   626  	}
   627  	// Mix in random dataset nodes
   628  	// 定义一个temp,与mix结构相同,长度相同
   629  	temp := make([]uint32, len(mix))
   630  
   631  	for i := 0; i < loopAccesses; i++ {
   632  		parent := fnv(uint32(i)^seedHead, mix[i%len(mix)]) % rows
   633  		for j := uint32(0); j < mixBytes/hashBytes; j++ {
   634  			copy(temp[j*hashWords:], lookup(2*parent+j))
   635  		}
   636  		// 将mix中所有元素都与temp中对应位置的元素进行FNV hash运算
   637  		fnvHash(mix, temp)
   638  	}
   639  	// Compress mix
   640  	// 对Mix进行混淆
   641  	for i := 0; i < len(mix); i += 4 {
   642  		mix[i/4] = fnv(fnv(fnv(mix[i], mix[i+1]), mix[i+2]), mix[i+3])
   643  	}
   644  	// 保留8个字节有效数据
   645  	mix = mix[:len(mix)/4]
   646  
   647  	// 将长度为8的mix分散到32位的digest中去
   648  	digest := make([]byte, common.HashLength)
   649  	for i, val := range mix {
   650  		binary.LittleEndian.PutUint32(digest[i*4:], val)
   651  	}
   652  	return digest, crypto.Keccak256(append(seed, digest...))
   653  }
   654  ```
   655  
   656  在hashimoto函数中涉及到一个fnv函数,这是一个哈谢算法,全名Fowler-Noll-Vo哈希算法,是以三位发明人名字命名的。FNV能快速hash大量数据并保持较小的冲突率,它的高度分散使它适用于hash一些非常相近的字符串,比如URL,hostname,文件名,text,IP地址等。
   657  
   658  ```
   659  // fnv is an algorithm inspired by the FNV hash, which in some cases is used as
   660  // a non-associative substitute for XOR. Note that we multiply the prime with
   661  // the full 32-bit input, in contrast with the FNV-1 spec which multiplies the
   662  // prime with one byte (octet) in turn.
   663  // fnv算法
   664  func fnv(a, b uint32) uint32 {
   665  	return a*0x01000193 ^ b
   666  }
   667  
   668  // fnvHash mixes in data into mix using the ethash fnv method.
   669  // fnv哈希算法
   670  func fnvHash(mix []uint32, data []uint32) {
   671  	for i := 0; i < len(mix); i++ {
   672  		mix[i] = mix[i]*0x01000193 ^ data[i]
   673  	}
   674  }
   675  ```
   676  
   677  至此就达到计算一个nonce的pow值得目的了,判断noce合法的条件是它小于一个给定的target。我们知道比特币出块的时间是10min左右,以太坊呢是12s左右。那么以太坊内部是怎么保证这个出块时间不会有太大的波动,这就需要来看看CalcDifficulty()方法。
   678  
   679  ```
   680  // CalcDifficulty is the difficulty adjustment algorithm. It returns
   681  // the difficulty that a new block should have when created at time
   682  // given the parent block's time and difficulty.
   683  // 难度调整算法,用来保障出块时间在一个固定值上下波动
   684  func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
   685  	return CalcDifficulty(chain.Config(), time, parent)
   686  }
   687  
   688  // CalcDifficulty is the difficulty adjustment algorithm. It returns
   689  // the difficulty that a new block should have when created at time
   690  // given the parent block's time and difficulty.
   691  // 计算在给定父块时间和难度下当前块应该具有的难度
   692  func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
   693  	next := new(big.Int).Add(parent.Number, big1)
   694  	switch {
   695  	// 以太坊不同阶段难度调整算法不同
   696  	case config.IsByzantium(next):
   697  		return calcDifficultyByzantium(time, parent)
   698  	case config.IsHomestead(next):
   699  		return calcDifficultyHomestead(time, parent)
   700  	default:
   701  		return calcDifficultyFrontier(time, parent)
   702  	}
   703  }
   704  ```
   705  
   706  目前以太坊处在Homestead阶段,所以就以calcDifficultyHomestead为例来看看内部调整难度的算法。其难度计算规则见[eip-2](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md/)。
   707  
   708  ```
   709  // calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
   710  // the difficulty that a new block should have when created at time given the
   711  // parent block's time and difficulty. The calculation uses the Homestead rules.
   712  // Homestead阶段的区块难度调整算法
   713  func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int {
   714  	//这里给出了Homestead阶段的当前难度计算规则
   715  	// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md
   716  	// algorithm:
   717  	// diff = (parent_diff +
   718  	//         (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
   719  	//        ) + 2^(periodCount - 2)
   720  	// 2^(periodCount - 2)表示指数难度调整组件
   721  
   722  	// 当前时间
   723  	bigTime := new(big.Int).SetUint64(time)
   724  	// 父块出块时间
   725  	bigParentTime := new(big.Int).Set(parent.Time)
   726  
   727  	// holds intermediate values to make the algo easier to read & audit
   728  	x := new(big.Int)
   729  	y := new(big.Int)
   730  
   731  	// 1 - (block_timestamp - parent_timestamp) // 10
   732  	x.Sub(bigTime, bigParentTime)
   733  	x.Div(x, big10)
   734  	x.Sub(big1, x)
   735  
   736  	// max(1 - (block_timestamp - parent_timestamp) // 10, -99)
   737  	if x.Cmp(bigMinus99) < 0 {
   738  		x.Set(bigMinus99)
   739  	}
   740  	// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
   741  	y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
   742  	x.Mul(y, x)
   743  	x.Add(parent.Difficulty, x)
   744  
   745  	// minimum difficulty can ever be (before exponential factor)
   746  	if x.Cmp(params.MinimumDifficulty) < 0 {
   747  		x.Set(params.MinimumDifficulty)
   748  	}
   749  	// for the exponential factor
   750  	periodCount := new(big.Int).Add(parent.Number, big1)
   751  	periodCount.Div(periodCount, expDiffPeriod)
   752  
   753  	// the exponential factor, commonly referred to as "the bomb"
   754  	// diff = diff + 2^(periodCount - 2)
   755  	if periodCount.Cmp(big1) > 0 {
   756  		y.Sub(periodCount, big2)
   757  		y.Exp(big2, y, nil)
   758  		x.Add(x, y)
   759  	}
   760  	return x
   761  }
   762  ```
   763  
   764  每次进行挖矿难度的计算是在prepare阶段,prepare函数是实现共识引擎的准备阶段。
   765  
   766  ```
   767  // Prepare implements consensus.Engine, initializing the difficulty field of a
   768  // header to conform to the ethash protocol. The changes are done inline.
   769  // 实现共识引擎的准备阶段
   770  func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) error {
   771  	parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
   772  	if parent == nil {
   773  		return consensus.ErrUnknownAncestor
   774  	}
   775  	header.Difficulty = ethash.CalcDifficulty(chain, header.Time.Uint64(), parent)
   776  	return nil
   777  }
   778  ```
   779  
   780  至此,ethash算法源码就分析完了。
   781  
   782  # clique算法
   783  
   784  ### 原理
   785  Clique算法又称Proof-of-Authortiy(PoA),是以太坊测试网Ropsten在经过一次DDos攻击之后,数家公司共同研究推出的共识引擎,它运行在以太坊测试网Kovan上。
   786  
   787  PoA共识的主要特点:
   788  
   789  - PoA是依靠预设好的授权节点(signers),负责产生block.
   790  - 可以由已授权的signer选举(投票超过50%)加入新的signer。
   791  - 即使存在恶意signer,他最多只能攻击连续块(数量是 (SIGNER_COUNT / 2) + 1) 中的1个,期间可以由其他signer投票踢出该恶意signer。
   792  - 可指定产生block的时间。
   793  
   794  抛开授权节点的选举算法,Clique原理同样可以用一个公式来表示:
   795  
   796  #### n = F(pr, h)
   797  
   798  其中,F()是一个数字签名函数(目前是ECDSA),pr是公钥(common.Address类型),h是被签名的内容(common.Hash类型),n是最后生成的签名(一个65bytes的字符串)。
   799  
   800  在Clique算法中,所有节点被分为两类:
   801  
   802  -  认证节点,类似于矿工节点
   803  - 非认证节点, 类似普通的只能同步的节点
   804  
   805  这两种节点的角色可以互换,这种互换是通过对proposal的投票完成的。
   806  
   807  - 任何节点N都可以提交一个Propose来申请成为signer,然后所有的signers集体投票
   808  
   809  - 一个Signer只能给节点N投一张票
   810  
   811  - 一张投票包括:投票节点地址,被投票节点地址,被投票节点认证状态
   812  
   813  - 每进入一个新的epoch,所有之前的pending投票都作废
   814  
   815  PoA的大概流程是这样的:
   816  
   817   - __1.创世区块中指定一组认证节点(signers), signer地址保存在区块Extra字段__
   818  
   819  - __2.开始mining后,初始指定的signers对block进行签名和广播,签名结果保存在Extra字段__
   820  
   821  - __3.由于可能发生signers改变,Extra字段更新已授权的signers__
   822  
   823  - __4.每一个高度上处于IN-TURN状态的signer签名的Block优先广播,OUT-OF-TURN状态的随机延时一段时间再进行广播__
   824  
   825  - __5.新的signer通过API接口发起proposal,该proposal复用blockHeader的coinbase字段(signerAddress)和nonce字段(0xffffffffffffffff)广播,原有signers对该proposal进行投票,票数过半该发起者成为真正的signer__
   826  
   827  - __6.如果需要踢出一个signer,所有signers对该踢出行为进行投票,同样票数过半,该signer变成普通节点__
   828  
   829  这里通过API接口发起proposal时对blockHeader字段的复用情况是这样的:
   830  
   831  - __针对genesisBlock extra字段包含:__
   832      - 32bytes前缀(extraVanity)
   833      - 所有认证用户signers地址
   834      - 65bytes后缀(extraSeal):用来保存signer签名
   835  
   836  - __针对其他Block:__
   837      - extra: extraVanity和extraSeal
   838      - Noce:添加signer(nonceAuthVote: 0xffffffffffffffff);移除signer(nonceDropVote: 0x0000000000000000)
   839      - coinbase:被投票节点地址
   840      - Difficulty:1--本block签名者(IN-TURN);2-非本block签名者(OUT-OF-TURN)
   841  
   842  ### 开撸源码
   843  
   844  首先还是来看clique共识中几个重要的数据结构。首先看clique自身:
   845  
   846  ```
   847  // Clique is the proof-of-authority consensus engine proposed to support the
   848  // Ethereum testnet following the Ropsten attacks.
   849  // Clique是在Ropsten攻击之后支持Ethereum testnet的权威证明共识引擎
   850  type Clique struct {
   851  	// 共识引擎配置
   852  	config *params.CliqueConfig // Consensus engine configuration parameters
   853  	// 用于存取检索点快照的数据库
   854  	db     ethdb.Database       // Database to store and retrieve snapshot checkpoints
   855  
   856  	// 最近区块的快照,用于加速快照重组
   857  	recents    *lru.ARCCache // Snapshots for recent block to speed up reorgs
   858  	// 最近区块的签名,用于加速挖矿
   859  	signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
   860  
   861  	// 当前提出的proposals列表
   862  	proposals map[common.Address]bool // Current list of proposals we are pushing
   863  
   864  	// signer地址
   865  	signer common.Address // Ethereum address of the signing key
   866  	// 签名函数
   867  	signFn SignerFn       // Signer function to authorize hashes with
   868  	// 读写锁
   869  	lock   sync.RWMutex   // Protects the signer fields
   870  }
   871  ```
   872  
   873  共识引擎配置的结构:
   874  
   875  ```
   876  
   877  // CliqueConfig is the consensus engine configs for proof-of-authority based sealing.
   878  // Clique共识引擎配置
   879  type CliqueConfig struct {
   880  	// 距离上一区块出块后的时间间隔(s)
   881  	Period uint64 `json:"period"` // Number of seconds between blocks to enforce
   882  	// 重置投票和检查点的epoch长度
   883  	Epoch  uint64 `json:"epoch"`  // Epoch length to reset votes and checkpoint
   884  }
   885  ```
   886  
   887  snapshot是指定时间点的投票状态。
   888  
   889  ```
   890  // Snapshot is the state of the authorization voting at a given point in time.
   891  // 指定时间点的投票状态
   892  type Snapshot struct {
   893  	// clique共识配置
   894  	config   *params.CliqueConfig // Consensus engine parameters to fine tune behavior
   895  	// 最近区块签名的缓存,为了加速恢复
   896  	sigcache *lru.ARCCache        // Cache of recent block signatures to speed up ecrecover
   897  
   898  	// 快照建立的区块号
   899  	Number  uint64                      `json:"number"`  // Block number where the snapshot was created
   900  	// 区块hash
   901  	Hash    common.Hash                 `json:"hash"`    // Block hash where the snapshot was created
   902  	// 当下Signer的集合
   903  	Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
   904  	// 最近签名区块地址的集合
   905  	Recents map[uint64]common.Address   `json:"recents"` // Set of recent signers for spam protections
   906  	// 按顺序排列的投票列表
   907  	Votes   []*Vote                     `json:"votes"`   // List of votes cast in chronological order
   908  	// 当前投票结果,可以避免重新计算
   909  	Tally   map[common.Address]Tally    `json:"tally"`   // Current vote tally to avoid recalculating
   910  }
   911  ```
   912  
   913  其中,Vote是授权签名者为修改授权列表而进行的单一投票;Tally是Tally是一个简单的投票结果,以保持当前的投票得分。
   914  
   915  ```
   916  // Vote represents a single vote that an authorized signer made to modify the
   917  // list of authorizations.
   918  // 授权签名者为修改授权列表而进行的单一投票
   919  type Vote struct {
   920  	// 提出投票的signer
   921  	Signer    common.Address `json:"signer"`    // Authorized signer that cast this vote
   922  	// 投票所在的区块编号
   923  	Block     uint64         `json:"block"`     // Block number the vote was cast in (expire old votes)
   924  	// 被投票更改认证状态的地址
   925  	Address   common.Address `json:"address"`   // Account being voted on to change its authorization
   926  	// 是否授权或取消对已投票帐户的授权
   927  	Authorize bool           `json:"authorize"` // Whether to authorize or deauthorize the voted account
   928  }
   929  
   930  // Tally is a simple vote tally to keep the current score of votes. Votes that
   931  // go against the proposal aren't counted since it's equivalent to not voting.
   932  // 一个简单的投票结果,以保持当前的投票得分。
   933  type Tally struct {
   934  	// 投票是关于授权新的signer还是踢掉signer
   935  	Authorize bool `json:"authorize"` // Whether the vote is about authorizing or kicking someone
   936  	// 到目前为止希望通过提案的投票数
   937  	Votes     int  `json:"votes"`     // Number of votes until now wanting to pass the proposal
   938  }
   939  ```
   940  
   941  接下来,同样进入worker.agent.engine.seal()代码来看看PoA共识的实现逻辑。
   942  
   943  ```
   944  // Seal implements consensus.Engine, attempting to create a sealed block using
   945  // the local signing credentials.
   946  // 实现consensus.Engine
   947  func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
   948  	header := block.Header()
   949  
   950  	// Sealing the genesis block is not supported
   951  	number := header.Number.Uint64()
   952  	// 当前区块是创世区块
   953  	if number == 0 {
   954  		return nil, errUnknownBlock
   955  	}
   956  	// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
   957  	// 不支持0-period的链
   958  	if c.config.Period == 0 && len(block.Transactions()) == 0 {
   959  		return nil, errWaitTransactions
   960  	}
   961  	// Don't hold the signer fields for the entire sealing procedure
   962  	// 不要在整个签名过程中持有签名字段
   963  	c.lock.RLock()
   964  	// 获取signer和签名方法
   965  	signer, signFn := c.signer, c.signFn
   966  	c.lock.RUnlock()
   967  
   968  	// Bail out if we're unauthorized to sign a block
   969  	// 获取快照
   970  	snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
   971  	if err != nil {
   972  		return nil, err
   973  	}
   974  	if _, authorized := snap.Signers[signer]; !authorized {
   975  		return nil, errUnauthorized
   976  	}
   977  	// If we're amongst the recent signers, wait for the next block
   978  	// 如果是最近的signers中的一员,等待下一个块
   979  	for seen, recent := range snap.Recents {
   980  		if recent == signer {
   981  			// Signer is among recents, only wait if the current block doesn't shift it out
   982  			// 当前区块没有踢出Signer则继续等待
   983  			if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit {
   984  				log.Info("Signed recently, must wait for others")
   985  				<-stop
   986  				return nil, nil
   987  			}
   988  		}
   989  	}
   990  	// Sweet, the protocol permits us to sign the block, wait for our time
   991  	// 执行到这说明协议允许我们来签名这个区块
   992  	delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
   993  	if header.Difficulty.Cmp(diffNoTurn) == 0 {
   994  		// It's not our turn explicitly to sign, delay it a bit
   995  		// 当前处于OUT-OF-TURN状态,随机一定时间延迟处理
   996  		wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
   997  		delay += time.Duration(rand.Int63n(int64(wiggle)))
   998  
   999  		log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
  1000  	}
  1001  	log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
  1002  
  1003  	select {
  1004  	case <-stop:
  1005  		// 停止信号
  1006  		return nil, nil
  1007  	case <-time.After(delay):
  1008  	}
  1009  	// Sign all the things!
  1010  	// 签名工作
  1011  	sighash, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes())
  1012  	if err != nil {
  1013  		return nil, err
  1014  	}
  1015  	// 更新区块头的extra字段
  1016  	copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
  1017  
  1018  	// 通过区块头组装新区块
  1019  	return block.WithSeal(header), nil
  1020  }
  1021  ```
  1022  
  1023  其中获取投票状态快照的方法为:
  1024  
  1025  ```
  1026  // snapshot retrieves the authorization snapshot at a given point in time.
  1027  // 获取投票状态快照
  1028  func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
  1029  	// Search for a snapshot in memory or on disk for checkpoints
  1030  	// 在内存或磁盘上检索一个快照以检查检查点
  1031  	var (
  1032  		// 区块头
  1033  		headers []*types.Header
  1034  		// 快照对象
  1035  		snap    *Snapshot
  1036  	)
  1037  	for snap == nil {
  1038  		// If an in-memory snapshot was found, use that
  1039  		// 如果一个内存里的快照被找到
  1040  		if s, ok := c.recents.Get(hash); ok {
  1041  			snap = s.(*Snapshot)
  1042  			break
  1043  		}
  1044  		// If an on-disk checkpoint snapshot can be found, use that
  1045  		// 如果一个磁盘检查点的快照被找到
  1046  		if number%checkpointInterval == 0 {
  1047  			// 从数据库中加载一个快照
  1048  			if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil {
  1049  				log.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash)
  1050  				snap = s
  1051  				break
  1052  			}
  1053  		}
  1054  		// If we're at block zero, make a snapshot
  1055  		// 处于创世区块,创建一个快照
  1056  		if number == 0 {
  1057  			genesis := chain.GetHeaderByNumber(0)
  1058  			if err := c.VerifyHeader(chain, genesis, false); err != nil {
  1059  				return nil, err
  1060  			}
  1061  			signers := make([]common.Address, (len(genesis.Extra)-extraVanity-extraSeal)/common.AddressLength)
  1062  			for i := 0; i < len(signers); i++ {
  1063  				copy(signers[i][:], genesis.Extra[extraVanity+i*common.AddressLength:])
  1064  			}
  1065  			// 创建新快照
  1066  			snap = newSnapshot(c.config, c.signatures, 0, genesis.Hash(), signers)
  1067  			if err := snap.store(c.db); err != nil {
  1068  				return nil, err
  1069  			}
  1070  			log.Trace("Stored genesis voting snapshot to disk")
  1071  			break
  1072  		}
  1073  		// No snapshot for this header, gather the header and move backward
  1074  		// 没有这个区块头的快照,则收集区块头并向后移动
  1075  		var header *types.Header
  1076  		if len(parents) > 0 {
  1077  			// If we have explicit parents, pick from there (enforced)
  1078  			// 如果有明确的父块,必须选出一个
  1079  			header = parents[len(parents)-1]
  1080  			if header.Hash() != hash || header.Number.Uint64() != number {
  1081  				return nil, consensus.ErrUnknownAncestor
  1082  			}
  1083  			parents = parents[:len(parents)-1]
  1084  		} else {
  1085  			// No explicit parents (or no more left), reach out to the database
  1086  			// 没有明确的父块
  1087  			header = chain.GetHeader(hash, number)
  1088  			if header == nil {
  1089  				return nil, consensus.ErrUnknownAncestor
  1090  			}
  1091  		}
  1092  		headers = append(headers, header)
  1093  		number, hash = number-1, header.ParentHash
  1094  	}
  1095  	// Previous snapshot found, apply any pending headers on top of it
  1096  	// 找到之前的快照,将所有pending的区块头放在它的前面
  1097  	for i := 0; i < len(headers)/2; i++ {
  1098  		headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
  1099  	}
  1100  	// 通过区块头生成新的快照
  1101  	snap, err := snap.apply(headers)
  1102  	if err != nil {
  1103  		return nil, err
  1104  	}
  1105  	// 将当前快照区块的hash存到recents中
  1106  	c.recents.Add(snap.Hash, snap)
  1107  
  1108  	// If we've generated a new checkpoint snapshot, save to disk
  1109  	// 如果生成了一个新的检查点快照,保存到磁盘上
  1110  	if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
  1111  		if err = snap.store(c.db); err != nil {
  1112  			return nil, err
  1113  		}
  1114  		log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
  1115  	}
  1116  	return snap, err
  1117  }
  1118  ```
  1119  
  1120  接着继续深入snap.apply函数:
  1121  
  1122  ```
  1123  
  1124  // apply creates a new authorization snapshot by applying the given headers to
  1125  // the original one.
  1126  // 根据区块头创建一个新的signer的快照
  1127  func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
  1128  	// Allow passing in no headers for cleaner code
  1129  	if len(headers) == 0 {
  1130  		return s, nil
  1131  	}
  1132  	// Sanity check that the headers can be applied
  1133  	// 对入参区块头做完整性检查
  1134  	for i := 0; i < len(headers)-1; i++ {
  1135  		if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
  1136  			return nil, errInvalidVotingChain
  1137  		}
  1138  	}
  1139  
  1140  	// 判断区块序号是否连续
  1141  	if headers[0].Number.Uint64() != s.Number+1 {
  1142  		return nil, errInvalidVotingChain
  1143  	}
  1144  	// Iterate through the headers and create a new snapshot
  1145  	// 遍历区块头数组并建立新的侉子好
  1146  	snap := s.copy()
  1147  
  1148  	for _, header := range headers {
  1149  		// Remove any votes on checkpoint blocks
  1150  		// 移除检查点快照上的任何投票
  1151  		number := header.Number.Uint64()
  1152  		if number%s.config.Epoch == 0 {
  1153  			snap.Votes = nil
  1154  			snap.Tally = make(map[common.Address]Tally)
  1155  		}
  1156  		// Delete the oldest signer from the recent list to allow it signing again
  1157  		// 移除投票票数过半,移除signer
  1158  		if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
  1159  			delete(snap.Recents, number-limit)
  1160  		}
  1161  		// Resolve the authorization key and check against signers
  1162  		// 解析授权密钥并检查签名者
  1163  		signer, err := ecrecover(header, s.sigcache)
  1164  		if err != nil {
  1165  			return nil, err
  1166  		}
  1167  		if _, ok := snap.Signers[signer]; !ok {
  1168  			return nil, errUnauthorized
  1169  		}
  1170  		for _, recent := range snap.Recents {
  1171  			if recent == signer {
  1172  				return nil, errUnauthorized
  1173  			}
  1174  		}
  1175  		// 记录signer为该区块的签名者
  1176  		snap.Recents[number] = signer
  1177  
  1178  		// Header authorized, discard any previous votes from the signer
  1179  		// 丢弃之前的投票
  1180  		for i, vote := range snap.Votes {
  1181  			if vote.Signer == signer && vote.Address == header.Coinbase {
  1182  				// Uncast the vote from the cached tally
  1183  				// 从缓存的计数中取消投票
  1184  				snap.uncast(vote.Address, vote.Authorize)
  1185  
  1186  				// Uncast the vote from the chronological list
  1187  				// 从时间顺序列表中取消投票
  1188  				snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
  1189  				break // only one vote allowed
  1190  			}
  1191  		}
  1192  		// Tally up the new vote from the signer
  1193  		// 从签名者那里获得新的投票
  1194  		var authorize bool
  1195  		switch {
  1196  		case bytes.Equal(header.Nonce[:], nonceAuthVote):
  1197  			authorize = true
  1198  		case bytes.Equal(header.Nonce[:], nonceDropVote):
  1199  			authorize = false
  1200  		default:
  1201  			return nil, errInvalidVote
  1202  		}
  1203  		if snap.cast(header.Coinbase, authorize) {
  1204  			snap.Votes = append(snap.Votes, &Vote{
  1205  				Signer:    signer,
  1206  				Block:     number,
  1207  				Address:   header.Coinbase,
  1208  				Authorize: authorize,
  1209  			})
  1210  		}
  1211  		// If the vote passed, update the list of signers
  1212  		// 投票通过,更新signers列表
  1213  		if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
  1214  
  1215  			// 投票是选举新signer
  1216  			if tally.Authorize {
  1217  				snap.Signers[header.Coinbase] = struct{}{}
  1218  			} else {
  1219  				// 投票是选移除signer
  1220  				delete(snap.Signers, header.Coinbase)
  1221  
  1222  				// Signer list shrunk, delete any leftover recent caches
  1223  				// 签名者列表缩小,删除任何剩余的最近缓存
  1224  				if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
  1225  					delete(snap.Recents, number-limit)
  1226  				}
  1227  				// Discard any previous votes the deauthorized signer cast
  1228  				// 放弃任何以前的授权签名者投票
  1229  				for i := 0; i < len(snap.Votes); i++ {
  1230  					if snap.Votes[i].Signer == header.Coinbase {
  1231  						// Uncast the vote from the cached tally
  1232  						snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
  1233  
  1234  						// Uncast the vote from the chronological list
  1235  						snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
  1236  
  1237  						i--
  1238  					}
  1239  				}
  1240  			}
  1241  			// Discard any previous votes around the just changed account
  1242  			// 放弃已更改授权状态的账户之前的投票
  1243  			for i := 0; i < len(snap.Votes); i++ {
  1244  				if snap.Votes[i].Address == header.Coinbase {
  1245  					snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
  1246  					i--
  1247  				}
  1248  			}
  1249  			delete(snap.Tally, header.Coinbase)
  1250  		}
  1251  	}
  1252  	snap.Number += uint64(len(headers))
  1253  	snap.Hash = headers[len(headers)-1].Hash()
  1254  
  1255  	return snap, nil
  1256  }
  1257  ```
  1258  
  1259  综上分析,只有认证节点才有权利出块,其他节点只能同步区块。每次出块时,都会创建一个snapshot快照来表示当前时间的投票状态,这里涉及到了基于投票的认证节点的维护机制。
  1260  
  1261  每次认证节点的改变都是通过api向外暴露的propose接口,然后所有的认证节点signers对该提议propose进行投票,超过半数通过投票,最后更新认证节点signer列表并将认证住状态发生改变的账户之前的投票做相应处理。
  1262  
  1263  同样clique也会涉及到出块时间的控制。
  1264  
  1265  ```
  1266  // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
  1267  // that a new block should have based on the previous blocks in the chain and the
  1268  // current signer.
  1269  func (c *Clique) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
  1270  	snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil)
  1271  	if err != nil {
  1272  		return nil
  1273  	}
  1274  	return CalcDifficulty(snap, c.signer)
  1275  }
  1276  
  1277  // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
  1278  // that a new block should have based on the previous blocks in the chain and the
  1279  // current signer.
  1280  // clique共识难度调整算法 当前块的难度基于前一个块和当前的签名者
  1281  func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
  1282  	if snap.inturn(snap.Number+1, signer) {
  1283  		return new(big.Int).Set(diffInTurn)
  1284  	}
  1285  	return new(big.Int).Set(diffNoTurn)
  1286  }
  1287  ...
  1288  // inturn returns if a signer at a given block height is in-turn or not.
  1289  // 给定高度块的签名者是否轮流
  1290  func (s *Snapshot) inturn(number uint64, signer common.Address) bool {
  1291  	signers, offset := s.signers(), 0
  1292  	for offset < len(signers) && signers[offset] != signer {
  1293  		offset++
  1294  	}
  1295  	return (number % uint64(len(signers))) == uint64(offset)
  1296  }
  1297  ```
  1298  
  1299  这里的出块难度实际上就是基于签名者的。如果当前区块的签名者是轮流签名的,那么当前signer可以立即签名一个区块(因为已经轮到这个signer签名了);反之,如果签名者不是轮流的,那么将会随机等待一段时间再签名这个区块(上个区块很可能就是这个singer签名的)。
  1300  
  1301  ```
  1302  func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) {
  1303  ...
  1304  // 执行到这说明协议允许我们来签名这个区块
  1305  	delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
  1306      // 出块难度值判断
  1307  	if header.Difficulty.Cmp(diffNoTurn) == 0 {
  1308  		// It's not our turn explicitly to sign, delay it a bit
  1309  		// 当前处于OUT-OF-TURN状态,随机一定时间延迟处理
  1310  		wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
  1311  		delay += time.Duration(rand.Int63n(int64(wiggle)))
  1312  
  1313  		log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
  1314  	}
  1315  ...
  1316  }
  1317  ```
  1318  
  1319  
  1320  
  1321  至此,以太坊两种共识引擎clique和ethash的实现源码就分析完毕了。
  1322  
  1323  
  1324